/*!	 valuenode_composite.cpp
**	 Implementation of the "Composite" valuenode conversion.
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2008 Chris Moore
**	Copyright (c) 2009 Nikita Kitaev
**  Copyright (c) 2011 Carlos López
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include "valuenode_composite.h"
#include "valuenode_const.h"
#include <stdexcept>
#include <synfig/general.h>
#include <synfig/localization.h>
#include <synfig/valuenode_registry.h>
#include "valuenode_radialcomposite.h"
#include <synfig/vector.h>
#include <synfig/color.h>
#include <synfig/segment.h>
#include <synfig/savecanvas.h>
#include <synfig/transformation.h>
#include <synfig/weightedvalue.h>
#include <synfig/pair.h>
#include <synfig/blinepoint.h>
#include <synfig/widthpoint.h>
#include <synfig/dashitem.h>

#endif

using namespace std;
using namespace etl;
using namespace synfig;

REGISTER_VALUENODE(ValueNode_Composite, RELEASE_VERSION_0_61_06, "composite", "Composite")

synfig::ValueNode_Composite::ValueNode_Composite(const ValueBase &value, Canvas::LooseHandle canvas):
    LinkableValueNode(value.get_type())
{
    Vocab ret(get_children_vocab());
    set_children_vocab(ret);
    Type &type(get_type());

    if (type == type_vector) {
        set_link("x", ValueNode_Const::create(value.get(Vector())[0]));
        set_link("y", ValueNode_Const::create(value.get(Vector())[1]));
    } else if (type == type_color) {
        set_link("r", ValueNode_Const::create(value.get(Color()).get_r()));
        set_link("g", ValueNode_Const::create(value.get(Color()).get_g()));
        set_link("b", ValueNode_Const::create(value.get(Color()).get_b()));
        set_link("a", ValueNode_Const::create(value.get(Color()).get_a()));
    } else if (type == type_segment) {
        set_link("p1", ValueNode_Const::create(value.get(Segment()).p1));
        set_link("t1", ValueNode_Const::create(value.get(Segment()).t1));
        set_link("p2", ValueNode_Const::create(value.get(Segment()).p2));
        set_link("t2", ValueNode_Const::create(value.get(Segment()).t2));
    } else if (type == type_bline_point) {
        BLinePoint bline_point(value.get(BLinePoint()));
        set_link("point", ValueNode_Const::create(bline_point.get_vertex()));
        set_link("width", ValueNode_Const::create(bline_point.get_width()));
        set_link("origin", ValueNode_Const::create(bline_point.get_origin()));
        set_link("split", ValueNode_Const::create(bline_point.get_split_tangent_both()));
        set_link("split_radius", ValueNode_Const::create(bline_point.get_split_tangent_radius()));
        set_link("split_angle", ValueNode_Const::create(bline_point.get_split_tangent_angle()));
        set_link("t1", ValueNode_RadialComposite::create(bline_point.get_tangent1()));
        set_link("t2", ValueNode_RadialComposite::create(bline_point.get_tangent2()));
    } else if (type == type_width_point) {
        WidthPoint wpoint(value.get(WidthPoint()));
        set_link("position", ValueNode_Const::create(wpoint.get_position()));
        set_link("width", ValueNode_Const::create(wpoint.get_width()));
        set_link("side_before", ValueNode_Const::create(wpoint.get_side_type_before()));
        set_link("side_after", ValueNode_Const::create(wpoint.get_side_type_after()));
        ValueNode_Const::Handle value_node;
        value_node = ValueNode_Const::Handle::cast_dynamic(ValueNode_Const::create(wpoint.get_lower_bound()));

        if (value_node) {
            value_node->set_static(true);
            set_link("lower_bound", value_node);
        }

        value_node = ValueNode_Const::Handle::cast_dynamic(ValueNode_Const::create(wpoint.get_upper_bound()));

        if (value_node) {
            value_node->set_static(true);
            set_link("upper_bound", value_node);
        }
    } else if (type == type_dash_item) {
        DashItem ditem(value.get(DashItem()));
        set_link("offset", ValueNode_Const::create(ditem.get_offset()));
        set_link("length", ValueNode_Const::create(ditem.get_length()));
        set_link("side_before", ValueNode_Const::create(ditem.get_side_type_before()));
        set_link("side_after", ValueNode_Const::create(ditem.get_side_type_after()));
    } else if (type == type_transformation) {
        Transformation transformation(value.get(Transformation()));
        set_link("offset", ValueNode_Const::create(transformation.offset));
        set_link("angle", ValueNode_Const::create(transformation.angle));
        set_link("skew_angle", ValueNode_Const::create(transformation.skew_angle));
        set_link("scale", ValueNode_Const::create(transformation.scale));
    } else if (dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type) != NULL) {
        types_namespace::TypeWeightedValueBase *t =
            dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type);
        set_link("weight", ValueNode_Const::create(t->extract_weight(value)));
        set_link("value", ValueNode_Const::create(t->extract_value(value)));
    } else if (dynamic_cast<types_namespace::TypePairBase*>(&type) != NULL) {
        types_namespace::TypePairBase *t =
            dynamic_cast<types_namespace::TypePairBase*>(&type);
        set_link("first", ValueNode_Const::create(t->extract_first(value)));
        set_link("second", ValueNode_Const::create(t->extract_second(value)));
    } else {
        assert(0);
        throw Exception::BadType(get_type().description.local_name);
    }

    if (getenv("SYNFIG_DEBUG_SET_PARENT_CANVAS")) {
        printf("%s:%d set parent canvas for composite %lx to %lx\n", __FILE__, __LINE__, uintptr_t(this), uintptr_t(canvas.get()));
    }

    set_parent_canvas(canvas);
}

ValueNode_Composite::~ValueNode_Composite()
{
    unlink_all();
}

ValueNode_Composite*
ValueNode_Composite::create(const ValueBase &value, Canvas::LooseHandle canvas)
{
    return new ValueNode_Composite(value, canvas);
}

LinkableValueNode*
ValueNode_Composite::create_new()const
{
    return new ValueNode_Composite(ValueBase(get_type()));
}

ValueBase
synfig::ValueNode_Composite::operator()(Time t)const
{
    if (getenv("SYNFIG_DEBUG_VALUENODE_OPERATORS")) {
        printf("%s:%d operator()\n", __FILE__, __LINE__);
    }

    Type &type(get_type());

    if (type == type_vector) {
        Vector vect;
        assert(components[0] && components[1]);
        vect[0] = (*components[0])(t).get(Vector::value_type());
        vect[1] = (*components[1])(t).get(Vector::value_type());
        return vect;
    } else if (type == type_color) {
        Color color;
        assert(components[0] && components[1] && components[2] && components[3]);
        color.set_r((*components[0])(t).get(Vector::value_type()));
        color.set_g((*components[1])(t).get(Vector::value_type()));
        color.set_b((*components[2])(t).get(Vector::value_type()));
        color.set_a((*components[3])(t).get(Vector::value_type()));
        return color;
    } else if (type == type_segment) {
        Segment seg;
        assert(components[0] && components[1] && components[2] && components[3]);
        seg.p1 = (*components[0])(t).get(Point());
        seg.t1 = (*components[1])(t).get(Vector());
        seg.p2 = (*components[2])(t).get(Point());
        seg.t2 = (*components[3])(t).get(Vector());
        return seg;
    } else if (type == type_bline_point) {
        BLinePoint ret;
        assert(components[0] && components[1] && components[2] && components[3] && components[4] && components[5] && components[6] && components[7]);
        ret.set_vertex((*components[0])(t).get(Point()));
        ret.set_width((*components[1])(t).get(Real()));
        ret.set_origin((*components[2])(t).get(Real()));
        ret.set_split_tangent_both((*components[3])(t).get(bool()));
        ret.set_split_tangent_radius((*components[6])(t).get(bool()));
        ret.set_split_tangent_angle((*components[7])(t).get(bool()));
        ret.set_tangent1((*components[4])(t).get(Vector()));
        ret.set_tangent2((*components[5])(t).get(Vector()));
        return ret;
    } else if (type == type_width_point) {
        WidthPoint ret;
        assert(components[0] && components[1] && components[2] && components[3] && components[4] && components[5]);
        ret.set_position((*components[0])(t).get(Real()));
        ret.set_width((*components[1])(t).get(Real()));
        ret.set_side_type_before((*components[2])(t).get(int()));
        ret.set_side_type_after((*components[3])(t).get(int()));
        ret.set_lower_bound((*components[4])(t).get(Real()));
        ret.set_upper_bound((*components[5])(t).get(Real()));
        return ret;
    } else if (type == type_dash_item) {
        DashItem ret;
        assert(components[0] && components[1] && components[2] && components[3]);
        Real offset((*components[0])(t).get(Real()));

        if (offset < 0.0) {
            offset = 0.0;
        }

        Real length((*components[1])(t).get(Real()));

        if (length < 0.0) {
            length = 0.0;
        }

        ret.set_offset(offset);
        ret.set_length(length);
        ret.set_side_type_before((*components[2])(t).get(int()));
        ret.set_side_type_after((*components[3])(t).get(int()));
        return ret;
    } else if (type == type_transformation) {
        Transformation ret;
        assert(components[0] && components[1] && components[2] && components[3]);
        ret.offset    = (*components[0])(t).get(Vector());
        ret.angle     = (*components[1])(t).get(Angle());
        ret.skew_angle = (*components[2])(t).get(Angle());
        ret.scale     = (*components[3])(t).get(Vector());
        return ret;
    } else if (dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type) != NULL) {
        types_namespace::TypeWeightedValueBase *tp =
            dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type);
        assert(components[0] && components[1]);
        return tp->create_weighted_value((*components[0])(t).get(Real()), (*components[1])(t));
    } else if (dynamic_cast<types_namespace::TypePairBase*>(&type) != NULL) {
        types_namespace::TypePairBase *tp =
            dynamic_cast<types_namespace::TypePairBase*>(&type);
        assert(components[0] && components[1]);
        return tp->create_value((*components[0])(t), (*components[1])(t));
    }

    synfig::error(string("ValueNode_Composite::operator():") + _("Bad type for composite"));
    assert(components[0]);
    return (*components[0])(t);
}

bool
ValueNode_Composite::set_link_vfunc(int i, ValueNode::Handle x)
{
    assert(i >= 0 && i < link_count());

    if (PlaceholderValueNode::Handle::cast_dynamic(x)) {
        components[i] = x;
        return true;
    }

    Type &type(get_type());

    if (type == type_vector) {
        if (x->get_type() == ValueBase(Real()).get_type() || PlaceholderValueNode::Handle::cast_dynamic(x)) {
            components[i] = x;
            return true;
        }
    } else if (type == type_color) {
        if (x->get_type() == ValueBase(Real()).get_type() || PlaceholderValueNode::Handle::cast_dynamic(x)) {
            components[i] = x;
            return true;
        }
    } else if (type == type_segment) {
        if (x->get_type() == ValueBase(Point()).get_type() || PlaceholderValueNode::Handle::cast_dynamic(x)) {
            components[i] = x;
            return true;
        }
    } else if (type == type_bline_point) {
        if ((i == 0 || i == 4 || i == 5) && x->get_type() == ValueBase(Point()).get_type()) {
            components[i] = x;
            return true;
        }

        if ((i == 1 || i == 2) && x->get_type() == ValueBase(Real()).get_type()) {
            components[i] = x;
            return true;
        }

        if ((i == 3 || i == 6 || i == 7) && x->get_type() == ValueBase(bool()).get_type()) {
            components[i] = x;
            return true;
        }
    } else if (type == type_dash_item
               || type == type_width_point) {
        if ((i == 0 || i == 1) && x->get_type() == ValueBase(Real()).get_type()) {
            components[i] = x;
            return true;
        }

        if ((i == 2 || i == 3) && x->get_type() == ValueBase(int()).get_type()) {
            components[i] = x;
            return true;
        }

        if ((i == 4 || i == 5) && x->get_type() == ValueBase(Real()).get_type()) {
            if (ValueNode_Const::Handle::cast_dynamic(x)) {
                if (i == 4 && components[5]) {
                    if (i == 4 && (*x)(0).get(Real()) < (*components[5])(0).get(Real())) {
                        components[i] = x;
                        return true;
                    } else {
                        return false;
                    }
                }

                if (i == 5 && components[4]) {
                    if ((i == 5 && (*x)(0).get(Real()) > (*components[4])(0).get(Real()))) {
                        components[i] = x;
                        return true;
                    } else {
                        return false;
                    }
                }

                components[i] = x;
                return true;
            }

            return false;
        }
    } else if (type == type_transformation) {
        if (PlaceholderValueNode::Handle::cast_dynamic(x)
                || (i == 0 && x->get_type() == ValueBase(Vector()).get_type())
                || (i == 1 && x->get_type() == ValueBase(Angle()).get_type())
                || (i == 2 && x->get_type() == ValueBase(Angle()).get_type())
                || (i == 3 && x->get_type() == ValueBase(Vector()).get_type())
           ) {
            components[i] = x;
            return true;
        }
    } else if (dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type) != NULL) {
        types_namespace::TypeWeightedValueBase *tp =
            dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type);

        if (PlaceholderValueNode::Handle::cast_dynamic(x)
                || (i == 0 && x->get_type() == ValueBase(Real()).get_type())
                || (i == 1 && x->get_type() == tp->get_contained_type())
           ) {
            components[i] = x;
            return true;
        }
    } else if (dynamic_cast<types_namespace::TypePairBase*>(&type) != NULL) {
        types_namespace::TypePairBase *tp =
            dynamic_cast<types_namespace::TypePairBase*>(&type);

        if (PlaceholderValueNode::Handle::cast_dynamic(x)
                || (i == 0 && x->get_type() == tp->get_first_type())
                || (i == 1 && x->get_type() == tp->get_second_type())
           ) {
            components[i] = x;
            return true;
        }
    }

    return false;
}

ValueNode::LooseHandle
ValueNode_Composite::get_link_vfunc(int i)const
{
    assert(i >= 0 && i < link_count());

    return components[i];
}

String
ValueNode_Composite::link_name(int i)const
{
    assert(i >= 0 && i < link_count());

    if (get_file_version() < RELEASE_VERSION_0_61_08) {
        return strprintf("c%d", i + 1);
    }

    return LinkableValueNode::link_name(i);
}

int
ValueNode_Composite::get_link_index_from_name(const String &name)const
{
    // Here we don't use the LinkableValueNode::get_link_index_from_name
    // due to the particularities of the link index from name for old files.
    // So we keep this alive to maintain old file compatibilities.
    if (name.empty()) {
        throw Exception::BadLinkName(name);
    }

    if (name[0] == 'c' && name.size() == 2 && name[1] - '1' >= 0 && name[1] - '1' < link_count()) {
        return name[1] - '1';
    }

    Type &type(get_type());

    if (type == type_color) {
        if (name[0] == 'r') {
            return 0;
        }

        if (name[0] == 'g') {
            return 1;
        }

        if (name[0] == 'b') {
            return 2;
        }

        if (name[0] == 'a') {
            return 3;
        }
    } else if (type == type_segment) {
        if (name == "p1") {
            return 0;
        }

        if (name == "t1") {
            return 1;
        }

        if (name == "p2") {
            return 2;
        }

        if (name == "t2") {
            return 3;
        }
    } else if (type == type_vector) {
        if (name[0] == 'x') {
            return 0;
        }

        if (name[0] == 'y') {
            return 1;
        }

        if (name[0] == 'z') {	// TODO:  "z"?  really?
            return 2;
        }
    } else if (type == type_bline_point) {
        if (name[0] == 'p' || name == "v1" || name == "p1") {
            return 0;
        }

        if (name == "w" || name == "width") {
            return 1;
        }

        if (name == "o" || name == "origin") {
            return 2;
        }

        if (name == "split") {
            return 3;
        }

        if (name == "t1") {
            return 4;
        }

        if (name == "t2") {
            return 5;
        }

        if (name == "split_radius") {
            return 6;
        }

        if (name == "split_angle") {
            return 7;
        }
    } else if (type == type_width_point) {
        if (name == "position") {
            return 0;
        }

        if (name == "width") {
            return 1;
        }

        if (name == "side_before") {
            return 2;
        }

        if (name == "side_after") {
            return 3;
        }

        if (name == "lower_bound") {
            return 4;
        }

        if (name == "upper_bound") {
            return 5;
        }
    } else if (type == type_dash_item) {
        if (name == "offset") {
            return 0;
        }

        if (name == "length") {
            return 1;
        }

        if (name == "side_before") {
            return 2;
        }

        if (name == "side_after") {
            return 3;
        }
    } else if (type == type_transformation) {
        if (name == "offset") {
            return 0;
        }

        if (name == "angle") {
            return 1;
        }

        if (name == "skew_angle") {
            return 2;
        }

        if (name == "scale") {
            return 3;
        }
    } else if (dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type) != NULL) {
        if (name == "weight") {
            return 0;
        }

        if (name == "value") {
            return 1;
        }
    } else if (dynamic_cast<types_namespace::TypePairBase*>(&type) != NULL) {
        if (name == "first") {
            return 0;
        }

        if (name == "second") {
            return 1;
        }
    }

    throw Exception::BadLinkName(name);
}

bool
ValueNode_Composite::check_type(Type &type)
{
    return type == type_segment
           || type == type_vector
           || type == type_color
           || type == type_bline_point
           || type == type_width_point
           || type == type_dash_item
           || type == type_transformation
           || dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type) != NULL
           || dynamic_cast<types_namespace::TypePairBase*>(&type) != NULL;
}

LinkableValueNode::Vocab
ValueNode_Composite::get_children_vocab_vfunc()const
{
    if (children_vocab.size()) {
        return children_vocab;
    }

    LinkableValueNode::Vocab ret;

    Type &type(get_type());

    if (type == type_color) {
        ret.push_back(ParamDesc(ValueBase(), "red")
                      .set_local_name(_("Red"))
                      .set_description(_("The red component of the color"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "green")
                      .set_local_name(_("Green"))
                      .set_description(_("The green component of the color"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "blue")
                      .set_local_name(_("Blue"))
                      .set_description(_("The blue component of the color"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "alpha")
                      .set_local_name(_("Alpha"))
                      .set_description(_("The alpha of the color"))
                     );
        return ret;
    } else if (type == type_segment) {
        ret.push_back(ParamDesc(ValueBase(), "p1")
                      .set_local_name(_("Vertex 1"))
                      .set_description(_("The first vertex of the segment"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "t1")
                      .set_local_name(_("Tangent 1"))
                      .set_description(_("The first tangent of the segment"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "p2")
                      .set_local_name(_("Vertex 2"))
                      .set_description(_("The second vertex of the segment"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "t2")
                      .set_local_name(_("Tangent 2"))
                      .set_description(_("The second tangent of the segment"))
                     );
        return ret;
    } else if (type == type_vector) {
        ret.push_back(ParamDesc(ValueBase(), "x")
                      .set_local_name(_("X-Axis"))
                      .set_description(_("The X-Axis component of the vector"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "y")
                      .set_local_name(_("Y-Axis"))
                      .set_description(_("The Y-Axis component of the vector"))
                     );
        return ret;
    } else if (type == type_bline_point) {
        ret.push_back(ParamDesc(ValueBase(), "point")
                      .set_local_name(_("Vertex"))
                      .set_description(_("The vertex of the Spline Point"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "width")
                      .set_local_name(_("Width"))
                      .set_description(_("The width of the Spline Point"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "origin")
                      .set_local_name(_("Origin"))
                      .set_description(_("Defines the Off and On position relative to neighbours"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "split")
                      .set_local_name(_("Split"))
                      .set_description(_("When checked, tangents are independent"))
                      .hidden()
                     );
        ret.push_back(ParamDesc(ValueBase(), "t1")
                      .set_local_name(_("Tangent 1"))
                      .set_description(_("The first tangent of the Spline Point"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "t2")
                      .set_local_name(_("Tangent 2"))
                      .set_description(_("The second tangent of the Spline Point"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "split_radius")
                      .set_local_name(_("Radius Split"))
                      .set_description(_("When checked, tangent's radii are independent"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "split_angle")
                      .set_local_name(_("Angle Split"))
                      .set_description(_("When checked, tangent's angles are independent"))
                     );
        return ret;
    } else if (type == type_width_point) {
        ret.push_back(ParamDesc(ValueBase(), "position")
                      .set_local_name(_("Position"))
                      .set_description(_("The [0,1] position of the Width Point over the Spline"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "width")
                      .set_local_name(_("Width"))
                      .set_description(_("The width of the Width Point"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "side_before")
                      .set_local_name(_("Side Type Before"))
                      .set_description(_("Defines the interpolation type of the width point"))
                      .set_hint("enum")
                      .add_enum_value(WidthPoint::TYPE_INTERPOLATE, "interpolate", _("Interpolate"))
                      .add_enum_value(WidthPoint::TYPE_ROUNDED, "rounded", _("Rounded Stop"))
                      .add_enum_value(WidthPoint::TYPE_SQUARED, "squared", _("Squared Stop"))
                      .add_enum_value(WidthPoint::TYPE_PEAK, "peak", _("Peak Stop"))
                      .add_enum_value(WidthPoint::TYPE_FLAT, "flat", _("Flat Stop"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "side_after")
                      .set_local_name(_("Side Type After"))
                      .set_description(_("Defines the interpolation type of the width point"))
                      .set_hint("enum")
                      .add_enum_value(WidthPoint::TYPE_INTERPOLATE, "interpolate", _("Interpolate"))
                      .add_enum_value(WidthPoint::TYPE_ROUNDED, "rounded", _("Rounded Stop"))
                      .add_enum_value(WidthPoint::TYPE_SQUARED, "squared", _("Squared Stop"))
                      .add_enum_value(WidthPoint::TYPE_PEAK, "peak", _("Peak Stop"))
                      .add_enum_value(WidthPoint::TYPE_FLAT, "flat", _("Flat Stop"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "lower_bound")
                      .set_local_name(_("Lower Boundary"))
                      .set_description(_("Defines the position at start of the Spline"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "upper_bound")
                      .set_local_name(_("Upper Boundary"))
                      .set_description(_("Defines the position at end of the Spline"))
                     );
        return ret;
    } else if (type == type_dash_item) {
        ret.push_back(ParamDesc(ValueBase(), "offset")
                      .set_local_name(_("Offset"))
                      .set_description(_("The offset length of the Dash Item over the Spline"))
                      .set_is_distance()
                     );
        ret.push_back(ParamDesc(ValueBase(), "length")
                      .set_local_name(_("Length"))
                      .set_description(_("The length of the Dash Item"))
                      .set_is_distance()
                     );
        ret.push_back(ParamDesc(ValueBase(), "side_before")
                      .set_local_name(_("Side Type Before"))
                      .set_description(_("Defines the side type of the dash item"))
                      .set_hint("enum")
                      .add_enum_value(WidthPoint::TYPE_ROUNDED, "rounded", _("Rounded Stop"))
                      .add_enum_value(WidthPoint::TYPE_SQUARED, "squared", _("Squared Stop"))
                      .add_enum_value(WidthPoint::TYPE_PEAK, "peak", _("Peak Stop"))
                      .add_enum_value(WidthPoint::TYPE_FLAT, "flat", _("Flat Stop"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "side_after")
                      .set_local_name(_("Side Type After"))
                      .set_description(_("Defines the side type of the dash item"))
                      .set_hint("enum")
                      .add_enum_value(WidthPoint::TYPE_ROUNDED, "rounded", _("Rounded Stop"))
                      .add_enum_value(WidthPoint::TYPE_SQUARED, "squared", _("Squared Stop"))
                      .add_enum_value(WidthPoint::TYPE_PEAK, "peak", _("Peak Stop"))
                      .add_enum_value(WidthPoint::TYPE_FLAT, "flat", _("Flat Stop"))
                     );
        return ret;
    } else if (type == type_transformation) {
        ret.push_back(ParamDesc(ValueBase(), "offset")
                      .set_local_name(_("Offset"))
                      .set_description(_("The Offset component of the transformation"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "angle")
                      .set_local_name(_("Angle"))
                      .set_description(_("The Angle component of the transformation"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "skew_angle")
                      .set_local_name(_("Skew Angle"))
                      .set_description(_("The Skew Angle component of the transformation"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "scale")
                      .set_local_name(_("Scale"))
                      .set_description(_("The Scale component of the transformation"))
                     );
        return ret;
    } else if (dynamic_cast<types_namespace::TypeWeightedValueBase*>(&type) != NULL) {
        ret.push_back(ParamDesc(ValueBase(), "weight")
                      .set_local_name(_("Weight"))
                      .set_description(_("The Weight of the value"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "value")
                      .set_local_name(_("Value"))
                      .set_description(_("The Value"))
                     );
        return ret;
    } else if (dynamic_cast<types_namespace::TypePairBase*>(&type) != NULL) {
        ret.push_back(ParamDesc(ValueBase(), "first")
                      .set_local_name(_("First"))
                      .set_description(_("The First Value"))
                     );
        ret.push_back(ParamDesc(ValueBase(), "second")
                      .set_local_name(_("Second"))
                      .set_description(_("The Second Value"))
                     );
        return ret;
    }

    return ret;
}